package com.sync.platform.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.Vector;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

/**
 * SSH utility for server connections
 */
public class SSHUtil {

    private static final Logger logger = LoggerFactory.getLogger(SSHUtil.class);

    /**
     * Create a new SSH session
     *
     * @param host Server host
     * @param port Server port
     * @param username Server username
     * @param password Server password
     * @param privateKeyPath SSH private key path (optional)
     * @return JSch Session object
     * @throws JSchException if error occurs during session creation
     */
    public static Session createSession(String host, int port, String username, String password, String privateKeyPath)
            throws JSchException {
        JSch jsch = new JSch();
        Session session;

        if (privateKeyPath != null && !privateKeyPath.isEmpty()) {
            jsch.addIdentity(privateKeyPath);
            session = jsch.getSession(username, host, port);
        } else {
            session = jsch.getSession(username, host, port);
            session.setPassword(password);
        }

        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);

        return session;
    }

    /**
     * Execute a command on the server
     *
     * @param session JSch Session
     * @param command Command to execute
     * @return Command output
     * @throws JSchException if error occurs during session connection
     * @throws IOException if error occurs during command execution
     */
    public static String executeCommand(Session session, String command) throws JSchException, IOException {
        if (!session.isConnected()) {
            session.connect();
        }

        ChannelExec channel = (ChannelExec) session.openChannel("exec");
        channel.setCommand(command);

        InputStream in = channel.getInputStream();
        channel.connect();

        String result = IOUtils.toString(in, StandardCharsets.UTF_8);

        channel.disconnect();
        return result;
    }

    /**
     * Upload a file to the server
     *
     * @param session JSch Session
     * @param localPath Local file path
     * @param remotePath Remote file path
     * @throws JSchException if error occurs during session connection
     * @throws SftpException if error occurs during file transfer
     * @throws FileNotFoundException if local file not found
     */
    public static void uploadFile(Session session, String localPath, String remotePath)
            throws JSchException, SftpException, FileNotFoundException {
        if (!session.isConnected()) {
            session.connect();
        }

        ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();

        File localFile = new File(localPath);
        if (!localFile.exists()) {
            throw new FileNotFoundException("Local file not found: " + localPath);
        }

        // Create parent directories if they don't exist
        String remoteDir = remotePath.substring(0, remotePath.lastIndexOf('/'));
        createRemoteDirectories(channel, remoteDir);

        channel.put(new FileInputStream(localFile), remotePath);
        channel.disconnect();
    }

    /**
     * Download a file from the server
     *
     * @param session JSch Session
     * @param remotePath Remote file path
     * @param localPath Local file path
     * @throws JSchException if error occurs during session connection
     * @throws SftpException if error occurs during file transfer
     */
    public static void downloadFile(Session session, String remotePath, String localPath)
            throws JSchException, SftpException {
        if (!session.isConnected()) {
            session.connect();
        }

        ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();

        // Create parent directories if they don't exist
        File localFile = new File(localPath);
        File localDir = localFile.getParentFile();
        if (localDir != null && !localDir.exists()) {
            localDir.mkdirs();
        }

        channel.get(remotePath, localPath);
        channel.disconnect();
    }

    /**
     * List files in a remote directory
     *
     * @param session JSch Session
     * @param remotePath Remote directory path
     * @return Vector of ChannelSftp.LsEntry objects
     * @throws JSchException if error occurs during session connection
     * @throws SftpException if error occurs during directory listing
     */
    @SuppressWarnings("unchecked")
    public static Vector<ChannelSftp.LsEntry> listFiles(Session session, String remotePath)
            throws JSchException, SftpException {
        if (!session.isConnected()) {
            session.connect();
        }

        ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();

        Vector<ChannelSftp.LsEntry> list = channel.ls(remotePath);
        channel.disconnect();

        return list;
    }

    /**
     * Create remote directories recursively
     *
     * @param channel SFTP Channel
     * @param remotePath Remote directory path
     * @throws SftpException if error occurs during directory creation
     */
    public static void createRemoteDirectories(ChannelSftp channel, String remotePath) throws SftpException {
        if (remotePath.equals("")) {
            return;
        }

        try {
            channel.cd(remotePath);
            return;
        } catch (SftpException e) {
            // Directory doesn't exist
        }

        int index = remotePath.lastIndexOf('/');
        if (index <= 0) {
            // Root directory
            channel.mkdir(remotePath);
            channel.cd(remotePath);
            return;
        }

        // Create parent directories first
        String parentPath = remotePath.substring(0, index);
        createRemoteDirectories(channel, parentPath);

        // Then create this directory
        channel.mkdir(remotePath);
        channel.cd(remotePath);
    }

    /**
     * Close the SSH session
     *
     * @param session JSch Session
     */
    public static void closeSession(Session session) {
        if (session != null && session.isConnected()) {
            session.disconnect();
        }
    }
}
